﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using GrayParrot.Core.Json;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using GrayParrot.Extensions.Queries;

namespace GrayParrot.Web.Formly
{
    public static class FormlyHelper
    {
        public static List<FieldItem> ToFormly(this IUseFormly obj)
        {
            List<FieldItem> retValue = null;

            retValue = GetFields(obj.GetType());

            return retValue;
        }

        public static List<FieldItem> ToFormly<T>(Type metadataType)
        {
            List<FieldItem> retValue = null;

            retValue = GetFields(typeof(T), metadataType);

            return retValue;
        }

        public static IEnumerable<FieldItem> AddExpressionProperty(this IEnumerable<FieldItem> fields, string fieldName, List<ExpressionProperty> expressionProperties)
        {
            FieldItem f = fields.Descendants<FieldItem>(x => x.Childs).Where(c => c.Key == fieldName).FirstOrDefault();
            if (f != null)
            {
                foreach (FieldItem fi in fields)
                {
                    if ((fi.Childs != null) && (fi.Childs.Count() > 0))
                    {
                        FieldItem fc = fi.Childs.Where(x => x.Key == fieldName).FirstOrDefault();
                        if (fc != null)
                        {
                            f = fc;
                            break;
                        }
                    }
                }
            }

            if(f != null)
            {
                if (f.ExpressionProperties == null)
                    f.ExpressionProperties = new ExpressionProperties();

                if (f.ExpressionProperties.Items == null)
                    f.ExpressionProperties.Items = new List<ExpressionProperty>();

                foreach (ExpressionProperty ep in expressionProperties)
                {
                    f.ExpressionProperties.Items.Add(ep);
                }
            }
            return fields;
        }

        public static IEnumerable<FieldItem> AddExpressionProperty(this IEnumerable<FieldItem> fields, string fieldName, ExpressionProperty expressionProperty)
        {
            FieldItem f = fields.Descendants<FieldItem>(x => x.Childs).Where(c => c.Key == fieldName).FirstOrDefault();
            if (f != null)
            {
                if(f.ExpressionProperties == null)
                    f.ExpressionProperties = new ExpressionProperties();

                if (f.ExpressionProperties.Items == null)
                    f.ExpressionProperties.Items = new List<ExpressionProperty>();

                f.ExpressionProperties.Items.Add(expressionProperty);
            }
            else
            {
                FieldItem m_fi = null;
                foreach (FieldItem fi in fields)
                {
                    if ((fi.Childs != null) && (fi.Childs.Count() > 0))
                    {
                        FieldItem fc = fi.Childs.Where(x => x.Key == fieldName).FirstOrDefault();
                        if (fc != null)
                        {
                            m_fi = fc;
                            break;
                        }
                    }
                }

                if (f.ExpressionProperties == null)
                    f.ExpressionProperties = new ExpressionProperties();

                if (m_fi.ExpressionProperties.Items == null)
                    m_fi.ExpressionProperties.Items = new List<ExpressionProperty>();

                m_fi.ExpressionProperties.Items.Add(expressionProperty);
            }
            return fields;
        }

        public static IEnumerable<FieldItem> Hide(this IEnumerable<FieldItem> fields, string fieldName)
        {
            FieldItem f = fields.Descendants<FieldItem>(x => x.Childs).Where(c=>c.Key == fieldName).FirstOrDefault();
            if (f != null)
            {
                f.HideExpression = "true"; // new JRaw(true);
            }
            else
            {
                FieldItem m_fi = null;
                foreach (FieldItem fi in fields)
                {
                    if ((fi.Childs != null) && (fi.Childs.Count() > 0))
                    {
                        FieldItem fc = fi.Childs.Where(x => x.Key == fieldName).FirstOrDefault();
                        if (fc != null)
                        {
                            m_fi = fc;
                            break;
                        }
                    }
                }

                m_fi.HideExpression = "true";
            }
            return fields;
        }

        public static IEnumerable<FieldItem> Hide(this IEnumerable<FieldItem> fields, string fieldName, string hideCallback)
        {
            FieldItem f = fields.Where(x => x.Key == fieldName).FirstOrDefault();
            if (f != null)
            {
                object m_func = null;
                string m_strFunc = string.Empty;

                m_strFunc = string.Format(@"function(){{ return {0}(); }}", hideCallback);
                m_func = new Newtonsoft.Json.Linq.JRaw(m_strFunc);

                f.HideExpression = m_func;
            }
            else
            {
                FieldItem m_fi = null;
                foreach (FieldItem fi in fields)
                {
                    if ((fi.Childs != null) && (fi.Childs.Count() > 0))
                    {
                        FieldItem fc = fi.Childs.Where(x => x.Key == fieldName).FirstOrDefault();
                        if (fc != null)
                        {
                            m_fi = fc;
                            break;
                        }
                    }
                }

                object m_func = null;
                string m_strFunc = string.Empty;

                m_strFunc = string.Format(@"function(){{ return {0}(); }}", hideCallback);
                m_func = new Newtonsoft.Json.Linq.JRaw(m_strFunc);

                f.HideExpression = m_func;
            }
            return fields;
        }

        public static IEnumerable<FieldItem> Disable(this IEnumerable<FieldItem> fields, string fieldName)
        {
            FieldItem f = fields.Where(x => x.Key == fieldName).FirstOrDefault();
            if (f != null)
            {
                f.TemplateOptions.Disabled = true;
            }
            else
            {
                FieldItem m_fi = null;
                foreach (FieldItem fi in fields)
                {
                    if ((fi.Childs != null) && (fi.Childs.Count() > 0))
                    {
                        FieldItem fc = fi.Childs.Where(x => x.Key == fieldName).FirstOrDefault();
                        if (fc != null)
                        {
                            m_fi = fc;
                            break;
                        }
                    }
                }

                m_fi.TemplateOptions.Disabled = true;
            }

            return fields;
        }

        public static IEnumerable<FieldItem> Disable(this IEnumerable<FieldItem> fields, string fieldName, string disableCallback)
        {
            FieldItem f = fields.Where(x => x.Key == fieldName).FirstOrDefault();
            if (f != null)
            {
                object m_func = null;
                string m_strFunc = string.Empty;

                m_strFunc = string.Format(@"function(){{ return {0}(); }}", disableCallback);
                m_func = new Newtonsoft.Json.Linq.JRaw(m_strFunc);

                ExpressionProperty m_expDisabled = new ExpressionProperty();
                m_expDisabled.PropertyName = "templateOptions.disabled";
                m_expDisabled.ExpressionCallback = new Newtonsoft.Json.Linq.JRaw(m_strFunc);

                f.ExpressionProperties.Items.Add(m_expDisabled);
            }
            else
            {
                FieldItem m_fi = null;
                foreach (FieldItem fi in fields)
                {
                    if ((fi.Childs != null) && (fi.Childs.Count() > 0))
                    {
                        FieldItem fc = fi.Childs.Where(x => x.Key == fieldName).FirstOrDefault();
                        if (fc != null)
                        {
                            m_fi = fc;
                            break;
                        }
                    }
                }

                object m_func = null;
                string m_strFunc = string.Empty;

                m_strFunc = string.Format(@"function(){{ return {0}(); }}", disableCallback);
                m_func = new Newtonsoft.Json.Linq.JRaw(m_strFunc);

                ExpressionProperty m_expDisabled = new ExpressionProperty();
                m_expDisabled.PropertyName = "templateOptions.disabled";
                m_expDisabled.ExpressionCallback = new Newtonsoft.Json.Linq.JRaw(m_strFunc);

                f.ExpressionProperties.Items.Add(m_expDisabled);
            }

            return fields;
        }

        public static IEnumerable<FieldItem> DefaultValue(this IEnumerable<FieldItem> fields, string fieldName, string value)
        {
            FieldItem f = fields.Where(x => x.Key == fieldName).FirstOrDefault();
            if (f != null)
            {
                f.DefaultValue = value;
            }
            else
            {
                FieldItem m_fi = null;
                foreach (FieldItem fi in fields)
                {
                    if ((fi.Childs != null) && (fi.Childs.Count() > 0))
                    {
                        FieldItem fc = fi.Childs.Where(x => x.Key == fieldName).FirstOrDefault();
                        if (fc != null)
                        {
                            m_fi = fc;
                            break;
                        }
                    }
                }

                m_fi.DefaultValue = value;
            }

            return fields;
        }

        public static IEnumerable<FieldItem> DefaultValue<T>(this IEnumerable<FieldItem> fields, string fieldName, T value)
        {
            FieldItem f = fields.Where(x => x.Key == fieldName).FirstOrDefault();
            if (f != null)
            {
                f.DefaultValue = (T)value;
            }
            else
            {
                FieldItem m_fi = null;
                foreach(FieldItem fi in fields)
                {
                    if ((fi.Childs != null) && (fi.Childs.Count() > 0))
                    {
                        FieldItem fc = fi.Childs.Where(x => x.Key == fieldName).FirstOrDefault();
                        if (fc != null)
                        {
                            m_fi = fc;
                            break;
                        }
                    }
                }

                m_fi.DefaultValue = (T)value;
            }

            return fields;
        }

        /// <summary>
        /// Serializes the specified fields...
        /// </summary>
        /// <param name="fields">The fields list...</param>
        /// <returns>The json rappresentation of the field items. Empty string if fails..</returns>
        /// <remarks>
        /// The serialization uses as default two converters:
        /// - StringEnumConverter
        /// - StringBooleanJsonConverter
        /// </remarks>
        public static string Serialize(IEnumerable<FieldItem> fields)
        {
            string retValue = string.Empty;

            JsonSerializerSettings settings = new JsonSerializerSettings();
            settings.ContractResolver = new NullToEmptyListResolver();
            settings.Converters = new List<JsonConverter> { 
                new Newtonsoft.Json.Converters.StringEnumConverter { CamelCaseText = false },
                new Newtonsoft.Json.Converters.ExpandoObjectConverter { }
                ,new StringBooleanJsonConverter()
                ,new EventsConverter()
                //,new ExpressionPropertiesConverter()
                //,new FunctionConverter()
            };
            settings.Formatting = Formatting.Indented;
            settings.NullValueHandling = NullValueHandling.Ignore;

            retValue = JsonConvert.SerializeObject(fields, settings);

            return retValue;
        }

        /// <summary>
        /// Serializes the specified fields...
        /// </summary>
        /// <param name="fields">The fields list...</param>
        /// <param name="converters">Cusotm additional converters...</param>
        /// <returns>The json rappresentation of the field items. Empty string if fails..</returns>
        /// <remarks>
        /// The serialization uses as default two converters:
        /// - StringEnumConverter
        /// - StringBooleanJsonConverter
        /// </remarks>
        public static string Serialize(IEnumerable<FieldItem> fields, List<JsonConverter> converters)
        {
            string retValue = string.Empty;

            // the conveters...
            List<JsonConverter> m_converters = new List<JsonConverter>();

            // custom additional converters...
            m_converters.AddRange(converters);

            // standard converters...
            m_converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter { CamelCaseText = false });
            m_converters.Add(new Newtonsoft.Json.Converters.ExpandoObjectConverter { });
            m_converters.Add(new StringBooleanJsonConverter());
            m_converters.Add(new EventsConverter());
            //m_converters.Add(new ExpressionPropertiesConverter());

            //
            JsonSerializerSettings settings = new JsonSerializerSettings();
            settings.ContractResolver = new NullToEmptyListResolver();
            settings.Converters = m_converters;
            settings.Formatting = Formatting.Indented;
            settings.NullValueHandling = NullValueHandling.Ignore;

            retValue = JsonConvert.SerializeObject(fields, settings);

            return retValue;
        }

        public static string ToJson(this IEnumerable<FieldItem> fields)
        {
            string retValue = string.Empty;

            JsonSerializerSettings settings = new JsonSerializerSettings();
            settings.ContractResolver = new NullToEmptyListResolver();
            settings.Converters = new List<JsonConverter> { 
                new Newtonsoft.Json.Converters.StringEnumConverter { CamelCaseText = false }
                ,new StringBooleanJsonConverter()
                //,new FunctionConverter()
            };
            settings.Formatting = Formatting.Indented;
            settings.NullValueHandling = NullValueHandling.Ignore;

            retValue = JsonConvert.SerializeObject(fields, settings);

            return retValue;
        }

        private static List<FieldItem> GetFields(Type type, Type metadataType)
        {
            List<FieldItem> retValue = null;

            AssociatedMetadataTypeTypeDescriptionProvider m_associatedMetadataTypeTypeDescriptionProvider = new AssociatedMetadataTypeTypeDescriptionProvider(type, metadataType);
            TypeDescriptor.AddProvider(m_associatedMetadataTypeTypeDescriptionProvider, type);

            PropertyDescriptorCollection propsDesc = TypeDescriptor.GetProperties(type);

            if (propsDesc.Count > 0)
            {
                retValue = new List<FieldItem>();

                foreach (PropertyDescriptor pd in propsDesc)
                {
                    if (pd.PropertyType.IsArray)
                    {
                        // check if is array...
                    }
                    else
                    {
                        // check if is list...
                        if (pd.PropertyType.IsGenericType && pd.PropertyType.GetGenericTypeDefinition() == typeof(List<>))
                        {
                            Type itemType = pd.PropertyType.GetGenericArguments()[0]; // use this...

                            // get the FieldItems for List<T> properties...
                            List<FieldItem> retValue2 = GetFields(itemType);

                            var attrs = pd.Attributes.OfType<FormFieldAttribute>(); //.GetCustomAttributes(true);

                            if (attrs.Any())
                            {
                                FieldItem m_item = new FieldItem();
                                m_item.Key = pd.Name;

                                FormFieldAttribute m_ffAtt = (FormFieldAttribute)attrs.FirstOrDefault();
                                if (m_ffAtt != null)
                                {
                                    m_item.Type = m_ffAtt.Type; //.ToString().ToLower();
                                }

                                if (m_item.Type == FormFieldTypes.RepeatSection)
                                {
                                    m_item.TemplateOptions = GetTemplateOptions(m_ffAtt.Type, pd.Attributes);

                                    // costruisci il campo fieds sulla base delle impostazioni necessarie al RepetSection
                                    RepeatSectionFieldItem m_rsfi = new RepeatSectionFieldItem();
                                    m_rsfi.ClassName = m_item.TemplateOptions.DrawDirection;
                                    m_rsfi.SetFieldGroup(retValue2);

                                    List<RepeatSectionFieldItem> m_fs = new List<RepeatSectionFieldItem>();
                                    m_fs.Add(m_rsfi);
                                    m_item.TemplateOptions.RSFields = m_fs;
                                }
                                else
                                {
                                    m_item.TemplateOptions = GetTemplateOptions(m_ffAtt.Type, pd.Attributes);
                                    m_item.TemplateOptions.Fields = retValue2;
                                }

                                retValue.Add(m_item);
                            }
                        }
                        else
                        {
                            // field is a primitive type...
                            FieldItem m_item = GetFieldItem(pd);

                            if (m_item != null)
                            {
                                var m_fgAttr = pd.Attributes.OfType<FieldGroupAttribute>().FirstOrDefault();
                                if (m_fgAttr != null)
                                {
                                    FieldItem m_itemFG = retValue.Where(x => x.FieldGroupName == m_fgAttr.GroupName).FirstOrDefault();

                                    int m_numberOfColumns = (m_fgAttr.NumberOfColumns == 1 ? 12 : m_fgAttr.NumberOfColumns);

                                    if (m_itemFG == null)
                                    {
                                        m_itemFG = new FieldItem();
                                        m_itemFG.Childs = new List<FieldItem>();
                                        m_itemFG.ClassName = "row";
                                        m_itemFG.FieldGroupName = m_fgAttr.GroupName;
                                        m_itemFG.Row = m_fgAttr.Row;

                                        m_item.ClassName = string.Format("col-xs-{0}", m_numberOfColumns);
                                        m_item.PositionInRow = m_fgAttr.Index;
                                        //m_item.Row = m_fgAttr.Row;

                                        // inserisce il campo nel gruppo di appartenenza...
                                        var pair = m_itemFG.Childs
                                                       .Select((value, index) => new { value, index })
                                                       .FirstOrDefault(x => (x.value.PositionInRow > m_item.PositionInRow));

                                        if (pair == null)
                                        {
                                            m_itemFG.Childs.Add(m_item);
                                        }
                                        else
                                        {
                                            m_itemFG.Childs.Insert((pair.index == 0 ? pair.index : pair.index-1), m_item);
                                        }

                                        // ordina i fileditems della lista dei campi...
                                        var m_row = retValue
                                               .Select((value, index) => new { value, index })
                                               .FirstOrDefault(x => (x.value.Row > m_itemFG.Row));

                                        if (m_row == null)
                                        {
                                            retValue.Add(m_itemFG);
                                        }
                                        else
                                        {
                                            retValue.Insert((m_row.index == 0 ? m_row.index : m_row.index - 1), m_itemFG);
                                        }
                                    }
                                    else
                                    {
                                        // campi raggruppati, allora inserisci nell'ordine giusto nella lista dei campi
                                        // appartenenti allo stesso gruppo...
                                        if (m_itemFG.Childs == null)
                                        {
                                            m_itemFG.Childs = new List<FieldItem>();
                                        }

                                        m_item.ClassName = string.Format("col-xs-{0}", m_numberOfColumns);
                                        m_item.PositionInRow = m_fgAttr.Index;
                                        m_item.Row = m_fgAttr.Row;

                                        var pair = m_itemFG.Childs
                                                       .Select((value, index) => new { value, index })
                                                       .FirstOrDefault(x => (x.value.PositionInRow > m_item.PositionInRow));

                                        if (pair == null)
                                        {
                                            m_itemFG.Childs.Add(m_item);
                                        }
                                        else
                                        {
                                            m_itemFG.Childs.Insert((pair.index == 0 ? pair.index : pair.index - 1), m_item);
                                        }
                                    }
                                }
                                else
                                {
                                    // i campi non sono raggruppati, quindi lista standard con visualizzazione verticale...
                                    retValue.Add(m_item);
                                }
                            }
                        }
                    }
                }
            }

            TypeDescriptor.RemoveProvider(m_associatedMetadataTypeTypeDescriptionProvider, type);

            return retValue;
        }

        private static List<FieldItem> GetFields(Type type)
        {
            List<FieldItem> retValue = null;

            TypeDescriptor.AddProvider(new AssociatedMetadataTypeTypeDescriptionProvider(type), type);

            PropertyDescriptorCollection propsDesc = TypeDescriptor.GetProperties(type);

            if (propsDesc.Count > 0)
            {
                retValue = new List<FieldItem>();

                foreach (PropertyDescriptor pd in propsDesc)
                {
                    if (pd.PropertyType.IsArray)
                    {
                        // check if is array...
                    }
                    else
                    {
                        // check if is list...
                        if (pd.PropertyType.IsGenericType && pd.PropertyType.GetGenericTypeDefinition() == typeof(List<>))
                        {
                            Type itemType = pd.PropertyType.GetGenericArguments()[0]; // use this...

                            // get the FieldItems for List<T> properties...
                            List<FieldItem> retValue2 = GetFields(itemType);

                            var attrs = pd.Attributes.OfType<FormFieldAttribute>(); //.GetCustomAttributes(true);

                            if (attrs.Any())
                            {
                                FieldItem m_item = new FieldItem();
                                m_item.Key = pd.Name;

                                FormFieldAttribute m_ffAtt = (FormFieldAttribute)attrs.FirstOrDefault();
                                if (m_ffAtt != null)
                                {
                                    m_item.Type = m_ffAtt.Type; //.ToString().ToLower();
                                }

                                m_item.TemplateOptions = GetTemplateOptions(m_ffAtt.Type, pd.Attributes);
                                m_item.TemplateOptions.Fields = retValue2;

                                retValue.Add(m_item);
                            }
                        }
                        else
                        {
                            // field is a primitive type...
                            FieldItem m_item = GetFieldItem(pd);
                            if (m_item != null)
                            {
                                retValue.Add(m_item);
                            }
                        }
                    }
                }
            }

            return retValue;
        }

        private static FieldItem GetFieldItem(PropertyDescriptor propertyInfo)
        {
            FieldItem retValue = null;

            var attrs = propertyInfo.Attributes.OfType<FormFieldAttribute>();

            if ((attrs != null) && (attrs.Count() > 0))
            {
                retValue = new FieldItem();
                retValue.Key = propertyInfo.Name;

                FormFieldAttribute m_ffAtt = (FormFieldAttribute)attrs.FirstOrDefault();

                if (m_ffAtt != null)
                {
                    retValue.Type = m_ffAtt.Type; //.ToString();//.ToLower();
                }

                TemplateOptions m_templOpt = GetTemplateOptions(m_ffAtt.Type, propertyInfo.Attributes);
                retValue.TemplateOptions = m_templOpt;
                retValue.DefaultValue = m_templOpt.DefaultValue;
                if (m_templOpt.Value.HasValue)
                {
                    retValue.DefaultValue = m_templOpt.Value.Value; //.ToString();
                }
                
                var m_toAddOns = propertyInfo.Attributes.OfType<AddOnAttribute>();
                if (m_toAddOns != null)
                {
                    foreach (AddOnAttribute addOn in m_toAddOns)
                    {
                        if (addOn.Position == Position.Left)
                        {
                            string f = null;
                            object m_func = null;

                            if (addOn.OnClick != null)
                            {
                                f = string.Format(@"function(options, scope){{ {0}(options, scope); }}", addOn.OnClick);
                                //f = string.Format(@"{0}", addOn.OnClick);
                                m_func = new Newtonsoft.Json.Linq.JRaw(f);
                            }

                            retValue.TemplateOptions.AddonLeft = new AddOn()
                            {
                                Class = addOn.Class,
                                Text = addOn.Text,
                                OnClick = (string.IsNullOrWhiteSpace(f) ? f : m_func.ToString())
                            };
                        }

                        if (addOn.Position == Position.Right)
                        {
                            string f = null;
                            object m_func = null;

                            if (addOn.OnClick != null)
                            {
                                f = string.Format(@"function(options, scope){{ {0}(options, scope); }}", addOn.OnClick);
                                //f = string.Format(@"{0}", addOn.OnClick);
                                m_func = new Newtonsoft.Json.Linq.JRaw(f);
                            }

                            retValue.TemplateOptions.AddonRight = new AddOn()
                            {
                                Class = addOn.Class,
                                Text = addOn.Text,
                                OnClick = (string.IsNullOrWhiteSpace(f) ? f : m_func.ToString())
                                //string.Format("function(options, scope) {{ {0}(options, scope); }}", addOn.OnClick)
                            };
                        }
                    }
                }

                var m_toEvents = propertyInfo.Attributes.OfType<EventsAttribute>();
                if (m_toEvents != null)
                {
                    string f = null;
                    object m_func = null;

                    foreach (EventsAttribute e in m_toEvents)
                    {
                        f = string.Format(@"function(options, scope){{ {0}(options, scope); }}", e.Callback);
                        m_func = new Newtonsoft.Json.Linq.JRaw(f);

                        retValue.TemplateOptions.Events.Add(new EventCallback() { Evento = e.Event, Callback = f });
                    }
                }
            }

            return retValue;
        }

        private static TemplateOptions GetTemplateOptions(FormFieldTypes fieldType, AttributeCollection attributes)
        {
            TemplateOptions retValue = null;

            switch (fieldType)
            {
                case FormFieldTypes.GrayParrotFileUpload:
                    {
                        GrayParrotFileUploadOptionsAttribute m_toAtt = GetAttribute<GrayParrotFileUploadOptionsAttribute>(attributes);

                        if (m_toAtt != null)
                        {
                            retValue = InternalHelper.CreateGrayParrotFileUpload(m_toAtt);
                        }
                    }
                    break;
                case FormFieldTypes.GrayParrotDateTimePicker:
                    {
                        GrayParrotDateTimePickerOptionsAttribute m_toAtt = GetAttribute<GrayParrotDateTimePickerOptionsAttribute>(attributes);

                        if (m_toAtt != null)
                        {
                            retValue = InternalHelper.CreateGrayParrotDateTimePicker(m_toAtt);
                        }
                    }
                    break;
                case FormFieldTypes.DateTimepicker:
                    {
                        DateTimeTemplateOptionsAttribute m_toAtt = GetAttribute<DateTimeTemplateOptionsAttribute>(attributes);

                        if (m_toAtt != null)
                        {
                            retValue = InternalHelper.CreateDateTimePicker(m_toAtt.Label, m_toAtt.Description, m_toAtt.Placeholder,
                                                                    m_toAtt.Disabled, m_toAtt.Required, new DataDatetimepickerConfig()
                                                                    {
                                                                        MinView = m_toAtt.MinView,
                                                                        StartView = m_toAtt.StartView,
                                                                        MinuteStep = m_toAtt.MinuteStep,
                                                                        RenderingMode = m_toAtt.RenderingMode
                                                                    });
                        }
                    }
                    break;
                case FormFieldTypes.MaskedInput:
                    {
                        MaskTemplateOptionsAttribute m_toAtt = GetAttribute<MaskTemplateOptionsAttribute>(attributes);

                        if (m_toAtt != null)
                        {
                            retValue = InternalHelper.CreateMaskedInput(m_toAtt.Label, m_toAtt.Description, m_toAtt.Placeholder, m_toAtt.Disabled, m_toAtt.Required, m_toAtt.Mask);
                        }
                    }
                    break;
                case FormFieldTypes.Timepicker:
                    {
                        TimeTemplateOptionsAttribute m_toAtt = GetAttribute<TimeTemplateOptionsAttribute>(attributes);

                        if (m_toAtt != null)
                        {
                            retValue = InternalHelper.CreateTimepicker(m_toAtt.Label, m_toAtt.Description, m_toAtt.Placeholder, m_toAtt.Disabled, m_toAtt.Required);
                        }
                    }
                    break;
                case FormFieldTypes.Datepicker:
                    {
                        DateTemplateOptionsAttribute m_toAtt = GetAttribute<DateTemplateOptionsAttribute>(attributes);

                        if (m_toAtt != null)
                        {
                            retValue = InternalHelper.CreateDatepicker(m_toAtt.Label, m_toAtt.Description, m_toAtt.Placeholder, m_toAtt.Disabled, m_toAtt.Required, m_toAtt.DatepickerPopup);
                        }
                    }
                    break;
                case FormFieldTypes.RepeatSection:
                    {
                        RepeatingSectionTemplateOptionsAttribute m_toAtt = GetAttribute<RepeatingSectionTemplateOptionsAttribute>(attributes);

                        if (m_toAtt != null)
                        {
                            retValue = InternalHelper.CreateRepeatSection(m_toAtt.Label, m_toAtt.Description, m_toAtt.Placeholder, m_toAtt.Disabled, m_toAtt.Required, m_toAtt.DrawDirection);
                        }
                    }
                    break;
                case FormFieldTypes.Input:
                    {
                        TextTemplateOptionsAttribute m_toAtt = GetAttribute<TextTemplateOptionsAttribute>(attributes);

                        if (m_toAtt != null)
                        {
                            retValue = InternalHelper.CreateInput(m_toAtt.Label, m_toAtt.Description, m_toAtt.Placeholder, m_toAtt.Disabled, m_toAtt.Required, m_toAtt.MinLength, m_toAtt.MaxLength);
                        }
                    }
                    break;
                case FormFieldTypes.Number:
                    {
                        NumericTemplateOptionsAttribute m_toAtt = GetAttribute<NumericTemplateOptionsAttribute>(attributes);

                        if (m_toAtt != null)
                        {
                            retValue = InternalHelper.CreateNumber(m_toAtt.Label, m_toAtt.Description, m_toAtt.Placeholder, m_toAtt.Disabled, m_toAtt.Required, m_toAtt.MinLength, m_toAtt.MaxLength, m_toAtt.Min, m_toAtt.MaxLength, m_toAtt.Value);
                        }
                        else
                        {
                            DecimalTemplateOptionsAttribute m_toAtt2 = GetAttribute<DecimalTemplateOptionsAttribute>(attributes);

                            retValue = InternalHelper.CreateDecimal(m_toAtt2.Label, m_toAtt2.Description, m_toAtt2.Placeholder, m_toAtt2.Disabled, m_toAtt2.Required, m_toAtt2.MinLength, m_toAtt2.MaxLength, m_toAtt2.Min, m_toAtt2.Max, m_toAtt2.Value, m_toAtt2.Step);
                        }
                    }
                    break;
                case FormFieldTypes.Textarea:
                    {
                        TextAreaTemplateOptionsAttribute m_toAtt = GetAttribute<TextAreaTemplateOptionsAttribute>(attributes);

                        if (m_toAtt != null)
                        {
                            retValue = InternalHelper.CreateTextArea(m_toAtt.Label, m_toAtt.Description, m_toAtt.Placeholder, m_toAtt.Disabled, m_toAtt.Required, m_toAtt.Rows, m_toAtt.Cols);
                        }
                    }
                    break;
                case FormFieldTypes.Checkbox:
                    {
                        CheckBoxTemplateOptionsAttribute m_toAtt = GetAttribute<CheckBoxTemplateOptionsAttribute>(attributes);

                        if (m_toAtt != null)
                        {
                            retValue = InternalHelper.CreateCheckBox(m_toAtt.Label, m_toAtt.Disabled, m_toAtt.Required, m_toAtt.DefaultValue);
                        }
                    }
                    break;
                case FormFieldTypes.MultiCheckbox:
                    {
                        MultiCheckBoxTemplateOptionsAttribute m_toAtt = GetAttribute<MultiCheckBoxTemplateOptionsAttribute>(attributes);

                        if (m_toAtt != null)
                        {
                            retValue = InternalHelper.CreateMultiCheckBox(m_toAtt.Label, 
                                m_toAtt.Description, 
                                m_toAtt.Placeholder, 
                                m_toAtt.Disabled, 
                                m_toAtt.Required, 
                                m_toAtt.Callback, 
                                m_toAtt.LabelProp, 
                                m_toAtt.ValueProp, 
                                m_toAtt.DependsBy, 
                                m_toAtt.ForceLoad);
                        }
                    }
                    break;
                case FormFieldTypes.Radio:
                    {
                        RadioTemplateOptionsAttribute m_toAtt = GetAttribute<RadioTemplateOptionsAttribute>(attributes);

                        if (m_toAtt != null)
                        {
                            retValue = InternalHelper.CreateRadio(m_toAtt.Label, m_toAtt.Description, m_toAtt.Placeholder, m_toAtt.Disabled, m_toAtt.Required, m_toAtt.Callback, m_toAtt.DefaultValue);
                        }
                    }
                    break;
                case FormFieldTypes.Select:
                    {
                        SelectTemplateOptionsAttribute m_toAtt = GetAttribute<SelectTemplateOptionsAttribute>(attributes);

                        if (m_toAtt != null)
                        {
                            retValue = InternalHelper.CreateSelect(m_toAtt.Label, m_toAtt.Description, m_toAtt.Placeholder, 
                                                                   m_toAtt.Disabled, m_toAtt.Required, m_toAtt.GroupProp, m_toAtt.Callback, 
                                                                   m_toAtt.LabelProp, m_toAtt.ValueProp, m_toAtt.DependsBy, 
                                                                   m_toAtt.ForceLoad);
                        }
                    }
                    break;
                case FormFieldTypes.UISelect2:
                    {
                        UISelect2TemplateOptionsAttribute m_toAtt = GetAttribute<UISelect2TemplateOptionsAttribute>(attributes);

                        if (m_toAtt != null)
                        {
                            retValue = InternalHelper.CreateUISelect2(m_toAtt.Label, m_toAtt.Description, m_toAtt.Placeholder,
                                                                   m_toAtt.Disabled, m_toAtt.Required, m_toAtt.GroupProp, m_toAtt.Callback,
                                                                   m_toAtt.LabelProp, m_toAtt.ValueProp, m_toAtt.DependsBy,
                                                                   m_toAtt.ForceLoad, m_toAtt.MinimumInputLength, m_toAtt.EditProp);

                            retValue.NgOptions = m_toAtt.NgOptions;
                        }
                    }
                    break;
                case FormFieldTypes.Image:
                    {
                        ImageTemplateOptionsAttribute m_toAtt = GetAttribute<ImageTemplateOptionsAttribute>(attributes);

                        if (m_toAtt != null)
                        {
                            retValue = InternalHelper.CreateImage(m_toAtt.Label, m_toAtt.Description, m_toAtt.Placeholder,
                                                                   m_toAtt.Disabled, m_toAtt.Required, m_toAtt.Folder);
                        }
                    }
                    break;
                case FormFieldTypes.Upload:
                    {
                        ImageTemplateOptionsAttribute m_toAtt = GetAttribute<ImageTemplateOptionsAttribute>(attributes);

                        if (m_toAtt != null)
                        {
                            retValue = InternalHelper.CreateImage(m_toAtt.Label, m_toAtt.Description, m_toAtt.Placeholder,
                                                                   m_toAtt.Disabled, m_toAtt.Required, m_toAtt.Folder);
                        }
                    }
                    break;
                default:
                    break;
            }

            return retValue;
        }

        private static T GetAttribute<T>(AttributeCollection attributes)
        {
            return attributes.OfType<T>().FirstOrDefault();
        }
    }
}
